/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 *
 * Data Pointer Class
 *		Provides a structured implementation / simulation of a memory pointer.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.IO;

namespace Adaptive.Foundation
{
    /// <summary>
    /// Provides a structured implementation / simulation of a memory pointer.
    /// </summary>
    /// <remarks>
    /// This class acts as a management tool for a byte array and as a bridge
    /// between a byte array and a <see cref="MemoryStream"/>.
    /// </remarks>
    public class DataPointer : DisposableObjectBase
    {
        /*------------------------------------------------------------------------
         * Private Member Declarations
         *----------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Data stored in memory.
        /// </summary>
        private byte[] _dataContent;
        /// <summary>
        /// Current position of data pointer.
        /// </summary>
        private int _currentPosition = -1;
        #endregion

        /*------------------------------------------------------------------------
		 * Constructor / Dispose / Destructor Methods
		 *----------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public DataPointer()
            : base()
        {
        }
        /// <summary>
        /// Initializes a new instance of the class to the specified memory size.
        /// </summary>
        /// <param name="memorySizeToAllocate">
        /// The number of bytes to allocated.
        /// </param>
        public DataPointer(int memorySizeToAllocate)
            : base()
        {
            _dataContent = new byte[memorySizeToAllocate];
            _dataContent.Initialize();
            _currentPosition = 0;
        }
        /// <summary>
        /// Initializes a new instance of the class to the specified array.
        /// </summary>
        /// <param name="dataContent">
        /// The byte array content to be managed.
        /// </param>
        public DataPointer(byte[] dataContent)
            : base()
        {
            _dataContent = new byte[dataContent.Length];
            Array.Copy(dataContent, 0, _dataContent, 0, dataContent.Length);
            _currentPosition = 0;
        }
        /// <summary>
        /// Initializes a new instance of the class to the specified array.
        /// </summary>
        /// <param name="dataContent">
        /// The byte array content to be managed.
        /// </param>
        /// <param name="startIndex">The index of the <i>dataContent</i> array 
        /// at which to begin copying.
        /// </param>
        /// <param name="length">The number of bytes to be copied.</param>
        public DataPointer(byte[] dataContent, int startIndex, int length)
            : base()
        {
            _dataContent = new byte[length];
            Array.Copy(dataContent, startIndex, _dataContent, 0, length);
            _currentPosition = 0;
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((disposing) && (!Disposed))
            {
                if (_dataContent != null)
                    Array.Clear(_dataContent, 0, _dataContent.Length);
            }
            _dataContent = null;
            base.Dispose(disposing);
            GC.SuppressFinalize(this);
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Public Properties
		 *----------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets a value indicating whether the caller can read from the 
        /// underlying pointer stream.
        /// </summary>
        /// <value>
        /// <b>true</b> if data can be read; otherwise, <b>false</b>.
        /// </value>
        public bool CanRead
        {
            get
            {
                bool returnValue = false;

                if ((_dataContent != null) && (_dataContent.Length > 0) &&
                    (_currentPosition >= 0) && (_currentPosition < _dataContent.Length))
                    returnValue = true;

                return returnValue;
            }
        }
        /// <summary>
        /// Gets the size of the data stored in memory.
        /// </summary>
        /// <value>
        /// The length of the underlying array.
        /// </value>
        public int Length
        {
            get
            {
                int returnValue = 0;

                if (_dataContent != null)
                    returnValue = _dataContent.Length;
                return returnValue;
            }
        }
        /// <summary>
        /// Gets the current position of the "pointer".
        /// </summary>
        /// <value>
        /// The numeric index position in the underlying byte array.
        /// </value>
        public int Position
        {
            get
            {
                return _currentPosition;
            }
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Public Methods / Functions
		 *----------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Removes all memory contents.
        /// </summary>
        public void Clear()
        {
            if (_dataContent != null)
                Array.Clear(_dataContent, 0, _dataContent.Length);
            _dataContent = null;
            _currentPosition = -1;
        }
        /// <summary>
        /// Creates a copy of the current object instance.
        /// </summary>
        /// <returns>
        /// A new <see cref="DataPointer"/> instance with the same underlying
        /// data.
        /// </returns>
        public virtual DataPointer Copy()
        {
            return new DataPointer(_dataContent);
        }
        /// <summary>
        /// Moves the current pointer position to the specified index location.
        /// </summary>
        /// <param name="position">
        /// The index of the internal byte array to move to.
        /// </param>
        public void MoveTo(int position)
        {
            if (_dataContent == null)
                _currentPosition = -1;
            else if ((position >= 0) && (position <= _dataContent.Length))
                _currentPosition = position;
            else
                _currentPosition = 0;
        }
        /// <summary>
        /// Moves the internal position pointer to the start of the data.
        /// </summary>
        public void MoveFirst()
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
                _currentPosition = 0;
            else
                _currentPosition = -1;
        }
        /// <summary>
        /// Moves the internal poisition pointer to the end of the data.
        /// </summary>
        public void MoveLast()
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
                _currentPosition = _dataContent.Length - 1;
            else
                _currentPosition = -1;
        }
        /// <summary>
        /// Advanced the internal poisition pointer by one.
        /// </summary>
        public void MoveNext()
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                _currentPosition++;
                if (_currentPosition >= _dataContent.Length)
                    _currentPosition = _dataContent.Length - 1;
            }
            else
                _currentPosition = -1;
        }
        /// <summary>
        /// Moves the internal position pointer backwards by one.
        /// </summary>
        public void MovePrevious()
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                _currentPosition--;
                if (_currentPosition < 0)
                    _currentPosition = 0;
            }
            else
                _currentPosition = -1;
        }

        /// <summary>
        /// Advanced the internal poisition pointer by the specified number of bytes.
        /// </summary>
        /// <param name="bytesToMove">
        /// The number of bytes to move the pointer.
        /// </param>
        public void MoveNext(int bytesToMove)
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                _currentPosition += bytesToMove;
                if (_currentPosition >= _dataContent.Length)
                    _currentPosition = _dataContent.Length - 1;
            }
            else
                _currentPosition = -1;
        }
        /// <summary>
        /// Moves the internal position pointer backwards by the specified
        /// number of bytes.
        /// </summary>
        /// <param name="bytesToMove">
        /// The number of bytes to move the pointer.
        /// </param>
        public void MovePrevious(int bytesToMove)
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                _currentPosition -= bytesToMove;
                if (_currentPosition < 0)
                    _currentPosition = 0;
            }
            else
                _currentPosition = -1;
        }
        /// <summary>
        /// Converts the internal data to a <see cref="MemoryStream"/> instance.
        /// </summary>
        /// <returns>
        /// A <see cref="MemoryStream"/> instance if there is data to be read;
        /// otherwise, returns <b>null</b>.
        /// </returns>
        public MemoryStream ToStream()
        {
            MemoryStream returnValue = null;

            if ((_dataContent != null) && (_dataContent.Length > 0))
                returnValue = new MemoryStream(_dataContent);

            return returnValue;
        }
        /// <summary>
        /// Returns the underlying array value.
        /// </summary>
        /// <returns>
        /// The underlying byte array.
        /// </returns>
        public byte[] ToArray()
        {
            return _dataContent;
        }
        /// <summary>
        /// Reads a single byte at the current pointer position, and does not 
        /// advance the pointer.
        /// </summary>
        /// <returns>The byte value at the current position from the underlying data.</returns>
        public byte Peek()
        {
            if ((_dataContent != null) && (_dataContent.Length > 0))
                return _dataContent[_currentPosition];
            else
                return 0;
        }
        /// <summary>
        /// Reads the next byte of data from the underlying array and advances
        ///  the pointer by one.
        /// </summary>
        /// <returns>
        /// A byte value.
        /// </returns>
        public byte ReadByte()
        {
            byte returnValue = 0;

            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                returnValue = _dataContent[_currentPosition];
                _currentPosition++;
                CorrectPosition();
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a 16-bit integer from the underlying array and advances
        ///  the pointer by two bytes.
        /// </summary>
        /// <returns>
        /// A short integer value.
        /// </returns>
        public short ReadInt16()
        {
            short returnValue = 0;
            byte left = 0;
            byte right = 0;

            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                unchecked
                {
                    left = ReadByte();
                    right = ReadByte();
                    returnValue = (short)((right << 8) | left);
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a 32-bit integer from the underlying array and advances
        ///  the pointer by four bytes.
        /// </summary>
        /// <returns>
        /// A short integer value.
        /// </returns>
        public int ReadInt32()
        {
            int returnValue = 0;
            byte[] data;

            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                unchecked
                {
                    data = new byte[4];
                    data[3] = ReadByte();
                    data[2] = ReadByte();
                    data[1] = ReadByte();
                    data[0] = ReadByte();
                    returnValue = data[0] << 32 |
                        data[1] << 16 |
                        data[2] << 8 |
                        data[3];

                }
            }
            data = null;

            return returnValue;
        }
        /// <summary>
        /// Reads a 64-bit integer from the underlying array and advances
        ///  the pointer by eight bytes.
        /// </summary>
        /// <returns>
        /// A short integer value.
        /// </returns>
        public long ReadInt64()
        {
            long returnValue = 0;

            if ((_dataContent != null) && (_dataContent.Length > 0))
            {
                unchecked
                {
                    returnValue = (uint)ReadInt32() << 32 | (uint)ReadInt32();
                }
            }
            return returnValue;
        }
        /// <summary>
        /// Reads a single byte as a char at the current pointer, 
        /// advancing the pointer byte one.
        /// </summary>
        /// <returns>
        /// The character representing the byte value at the
        /// current pointer position.
        /// </returns>
        public char ReadChar()
        {
            return (char)ReadByte();
        }
        /// <summary>
        /// Reads the specified number of bytes from the underlying array.
        /// </summary>
        /// <param name="numberOfBytes">
        /// The number of bytes to be read.
        /// </param>
        /// <returns>
        /// A byte array containing the result; otherwise, <b>null</b> if 
        /// the index position is invalid or no data can be read.
        /// </returns>
        public byte[] ReadBytes(int numberOfBytes)
        {
            byte[] returnValue = null;
            int count = 0;

            if ((_dataContent != null) && (_dataContent.Length >= numberOfBytes))
            {
                returnValue = new byte[numberOfBytes];
                for (count = 0; count < numberOfBytes; count++)
                    returnValue[count] = ReadByte();
            }

            return returnValue;
        }
        /// <summary>
        /// Resizes the internal storage array to the specified size and preserves 
        /// the original data content.
        /// </summary>
        /// <param name="newArraySize">
        /// The new size of the array in bytes.
        /// </param>
        public void ReDimPreserve(int newArraySize)
        {
            byte[] temp = null;

            if (_dataContent != null)
            {
                temp = new byte[_dataContent.Length];
                Array.Copy(_dataContent, 0, temp, 0, _dataContent.Length);
                Array.Clear(_dataContent, 0, _dataContent.Length);
            }

            _dataContent = new byte[newArraySize];
            _dataContent.Initialize();
            if (temp != null)
            {
                if (_dataContent.Length < temp.Length)
                    Array.Copy(temp, 0, _dataContent, 0, _dataContent.Length);
                else
                    Array.Copy(temp, 0, _dataContent, 0, temp.Length);
                Array.Clear(temp, 0, temp.Length);
            }
            temp = null;

            CorrectPosition();
        }
        /// <summary>
        /// Concatenates the supplied byte array to the current byte array.
        /// </summary>
        /// <param name="dataToAdd">
        /// The byte array to be appended.
        /// </param>
        public void Concat(byte[] dataToAdd)
        {
            int indexPos = 0;

            indexPos = _dataContent.Length;
            ReDimPreserve(_dataContent.Length + dataToAdd.Length);
            Array.Copy(dataToAdd, 0, _dataContent, indexPos, dataToAdd.Length);

        }
        /// <summary>
        /// Overloads the + operator to allow addition of object instances.
        /// </summary>
        /// <param name="original">The original item to which data is to be appended.</param>
        /// <param name="newItem">
        /// A <see cref="DataPointer"/> instance to be appended to the original.
        /// </param>
        public static DataPointer operator +(DataPointer original, DataPointer newItem)
        {
            original.Concat(newItem.ToArray());
            return original;
        }
        /// <summary>
        /// Overloads the + operator to allow addition of object instances.
        /// </summary>
        /// <param name="original">The original item to which data is to be appended.</param>
        /// <param name="newItem">
        /// A byte array to be appended to the original.
        /// </param>
        public static DataPointer operator +(DataPointer original, byte[] newItem)
        {
            original.Concat(newItem);
            return original;
        }
        /// <summary>
        /// Overloads the + operator to allow addition of object instances.
        /// </summary>
        /// <param name="original">The original item to which data is to be appended.</param>
        /// <param name="newItem">
        /// A byte to append to the <see cref="DataPointer"/> instance.
        /// </param>
        public static DataPointer operator +(DataPointer original, byte newItem)
        {
            original.Concat(new byte[1] { newItem });
            return original;
        }
        #endregion

        /*------------------------------------------------------------------------
		 * Private Methods / Functions
		 *----------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Validates and corrects the value of the current position pointer.
        /// </summary>
        private void CorrectPosition()
        {
            if ((_dataContent == null) || (_dataContent.Length == 0))
                _currentPosition = -1;
            else if (_currentPosition >= _dataContent.Length)
                _currentPosition = _dataContent.Length - 1;
            else if (_currentPosition < 0)
                _currentPosition = 0;
        }
        #endregion
    }
}
